﻿using System;
using System.Threading;
using System.Threading.Tasks;
using AIClient.Configs;
using AIClient.Utils;
using NFX.Erlang;

namespace AIClient.Gates
{
    public class MessageHub
    {
        private readonly ErlLocalNode      _node;
        private readonly ErlMbox           _mbox;
        private readonly ErlAtom           _serverNode;
        private readonly ErlAtom           _serverProcess;
        private readonly ErlPatternMatcher _matcher;
        private readonly ManualResetEvent  _manualResetEvent;

        public MessageHub()
        {
            var serverCfg = ConfigManager.ServerCfg;
            _serverNode    = new ErlAtom(serverCfg.ServerNode);
            _serverProcess = new ErlAtom(serverCfg.ServerProcess);

            _node = new ErlLocalNode(serverCfg.ClientNode, new ErlAtom(serverCfg.Cookie), false);
            _node.Start();
            _mbox             = _node.CreateMbox(serverCfg.ClientProcess);
            _matcher          = new ErlPatternMatcher();
            _manualResetEvent = new ManualResetEvent(false);
        }

        // 启动
        public ManualResetEvent Start()
        {
            Register();
            Task.Run((Action) Receive);
            Connect();
            return _manualResetEvent;
        }

        // 向服务器发送消息
        public void Send(IErlObject message)
        {
            Console.WriteLine("Send/1");
            _mbox.Send(_serverNode, _serverProcess, message);
        }

        // 向服务器指定进程发送消息
        public void Send(ErlAtom process, IErlObject message)
        {
            Console.WriteLine("Send/2");
            _mbox.Send(_serverNode, process, message);
        }

        // 接收消息
        private void Receive()
        {
            while (true)
            {
                var msg = _mbox.Receive();
                if (-1 == _matcher.Match(ref msg))
                    Logger.LogWarning($"收到无法匹配的消息{msg}");
            }
        }

        // Matcher注册
        private void Register()
        {
            _matcher.Add("stop",                         Stop);
            _matcher.Add("{dispatch, PlayerId, Binary}", Dispatch);
            _matcher.Add("{update_time, TimeStamp}",     UpdateTime);
            _matcher.Add("{player_login, PlayerId}",     PlayerLogin);
        }

        // 连接服务器
        private void Connect()
        {
            Send(new ErlTuple(new ErlAtom("connect"), _mbox.Self));
        }

        // 关闭
        private IErlObject Stop(ErlPatternMatcher.Pattern pattern
                              , IErlObject                obj
                              , ErlVarBind                bind
                              , params object[]           objects)
        {
            _node.WaitForCompleteStop();
            _manualResetEvent.Set();
            return obj;
        }

        // 消息派发
        private IErlObject Dispatch(ErlPatternMatcher.Pattern pattern
                                  , IErlObject                obj
                                  , ErlVarBind                bind
                                  , params object[]           objects)
        {
            var playerId = (ErlLong) bind["PlayerId"];
            var binary   = (ErlBinary) bind["Binary"];
            if (Manager.Instance.Players.TryGetValue(playerId, out var player))
                player.Dispatch(binary.Value);

            return obj;
        }

        // 更新时间
        private IErlObject UpdateTime(ErlPatternMatcher.Pattern pattern
                                    , IErlObject                obj
                                    , ErlVarBind                bind
                                    , params object[]           objects)
        {
            var timeStamp = (ErlLong) bind["TimeStamp"];
            Manager.Instance.UpdateTimeStamp((int) timeStamp);

            return obj;
        }


        // AI登录
        private IErlObject PlayerLogin(ErlPatternMatcher.Pattern pattern
                                     , IErlObject                obj
                                     , ErlVarBind                bind
                                     , params object[]           objects)
        {
            var playerId = (ErlLong) bind["PlayerId"];
            Manager.Instance.StartPlayer(playerId);

            return obj;
        }
    }
}